home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 February: Tool Chest / Apple Developer CD Series Tool Chest February 1996 (Apple Computer)(1996).iso / Tool Chest / Testing & Debugging / General tools / Audit app & dcmd / Src / LogManager.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-09-17  |  34.7 KB  |  1,365 lines  |  [TEXT/KAHL]

  1. /*                                    LogManager.c                                */
  2. /*
  3.  * LogManager.c
  4.  * Copyright © 1993 Apple Computer Inc. All rights reserved.
  5.  *
  6.  * These functions manage a logging display for error messages and other text.
  7.  * The log is implemented as a ListManager list that can hold nLogItems. This
  8.  * module is intentionally more-or-less self-contained so it can easily be
  9.  * exported to other applications.
  10.  */
  11. #include "LogManager.h"
  12. #ifndef THINK_C                /* MPW includes            */
  13. #include <Errors.h>
  14. #include <Script.h>
  15. #include <Types.h>
  16. #include <Files.h>
  17. #include <Resources.h>
  18. #include <QuickDraw.h>
  19. #include <Fonts.h>
  20. #include <Events.h>
  21. #include <Windows.h>
  22. #include <ToolUtils.h>
  23. #include <Memory.h>
  24. #include <Menus.h>
  25. #include <Lists.h>
  26. #include <Printing.h>
  27. #include <Dialogs.h>
  28. #include <StandardFile.h>
  29. #endif
  30. #include <Serial.h>
  31. #include <Printing.h>
  32. #include <Packages.h>
  33. #pragma segment LogManager
  34.  
  35. /*
  36.  * There is a 32000 byte maximum for the list, so don't
  37.  * make nLogLines too big.
  38.  */
  39. #ifndef nDefaultLogLines
  40. #define nDefaultLogLines    128
  41. #endif
  42. #define width(r)                ((r).right - (r).left)
  43. #define height(r)                ((r).bottom - (r).top)
  44. #ifndef TRUE
  45. #define TRUE                    1
  46. #define FALSE                    0
  47. #endif
  48. enum {
  49.     kScrollBarWidth     = 16,
  50.     kScrollBarOffset    = kScrollBarWidth - 1,
  51.     kActiveControl        = 0,        /* Normal button hilite                */
  52.     kDisabledControl    = 255        /* Disabled button hilite            */
  53. };
  54. /*
  55.  * When the horizontal scrollbar is at zero, the list is indented +4 pixels.
  56.  * When the horizontal scrollbar increases, the list indent decreases. This
  57.  * value is "by inspection," but we could extract it from the list record
  58.  * when the list is initially created.
  59.  */
  60. enum {
  61.     kZeroIndent            = 4
  62. };
  63. /*
  64.  * This sets a nominal value for the maximum cell width. This might be
  65.  * better provided as a user-settable parameter.
  66.  */
  67. #define kMaxHorizontalScroll    (CharWidth('M') * 255)
  68. static const char        endOfLine[1] = { 0x0D };    /* <CR>            */
  69.  
  70. /*
  71.  * Cheap 'n dirty pascal string copy routine.
  72.  */
  73. #ifndef pstrcpy
  74. #define pstrcpy(dst, src) do {                            \
  75.         StringPtr    _src = (src);                        \
  76.         BlockMove(_src, dst, _src[0] + 1);                \
  77.     } while (0)
  78. #endif
  79. /*
  80.  * Cheap 'n dirty pascal string concat.
  81.  */
  82. #ifndef pstrcat
  83. #define pstrcat(dst, src) do {                            \
  84.         StringPtr        _dst = (dst);                    \
  85.         StringPtr        _src = (src);                    \
  86.         short            _len;                            \
  87.         _len = 255 - _dst[0];                            \
  88.         if (_len > _src[0]) _len = _src[0];                \
  89.         BlockMove(&_src[1], &_dst[1] + _dst[0], _len);    \
  90.         _dst[0] += _len;                                \
  91.     } while (0)
  92. #endif
  93.  
  94. static OSErr                        AddStringToList(
  95.         ListHandle                        logListHandle,
  96.         ConstStr255Param                theString
  97.     );
  98. static pascal void                    ScrollLogAction(
  99.         register ControlHandle            theControl,
  100.         short                            partcode
  101.     );
  102. static void                            ScrollLogList(
  103.         ControlHandle                    theControl
  104.     );
  105. static StringHandle                    GetLogStringHandle(
  106.         ListHandle                        logListHandle,
  107.         short                            theRow
  108.     );
  109.  
  110. #define LIST            (**logListHandle)
  111. #define LOGINFO            (**((LogInfoHdl) (LIST.userHandle)))
  112. #define HSCROLL            (LOGINFO.hScroll)
  113. #define IS_COLOR(port)    (((((CGrafPtr) (port))->portVersion) & 0xC000) != 0)
  114. #define COLOR_LIST        (IS_COLOR(LIST.port))
  115.  
  116. /*
  117.  * This code sequence is copied into the ListProc handle. It is designed so we
  118.  * don't have to flush the instruction and data caches. Note that this must
  119.  * be revised if you compile for a non-68000 environment.
  120.  */
  121. static const short gDummyLDEF[] = {
  122.         0x207A,                /*        movea.l    procPtr,a0            */
  123.         0x0004,                /*                <offset to procPtr>    */
  124.         0x4ED0,                /*        jmp        a0                     */
  125.         0x0000, 0x0000        /*        dc.l    <Drawing Proc here>    */
  126. };
  127.  
  128. static pascal void
  129. LogListDefProc(
  130.         short                    listMessage,
  131.         Boolean                    listSelect,
  132.         Rect                    *listRect,
  133.         Cell                    listCell,
  134.         short                    listDataOffset,
  135.         short                    listDataLen,
  136.         ListHandle                errorLogList
  137.     );
  138.  
  139. /*
  140.  * Create the data display list.
  141.  */
  142. ListHandle
  143. CreateLog(
  144.         const Rect                    *viewRect,
  145.         short                        listFontNumber,
  146.         short                        listFontSize,
  147.         short                        logLines,
  148.         Boolean                        hasGrowBox
  149.     )
  150. {
  151.         OSErr                        status;
  152.         ListHandle                    logListHandle;
  153.         FontInfo                    info;
  154.         Point                        cellSize;
  155.         short                        listHeight;
  156.         Rect                        dataBounds;
  157.         Rect                        listRect;
  158.         short                        listFontHeight;
  159.         Handle                        drawProcHdl;
  160.         LogInfoRecord                logInfo;
  161.         Handle                        logInfoHdl;
  162.         ProcPtr                        listProcPtr;
  163.         short                        horizontalMax;
  164.         
  165.         logInfoHdl = NULL;
  166.         drawProcHdl = NULL;
  167.         if (logLines == 0)
  168.             logLines = nDefaultLogLines;
  169.         TextFont(listFontNumber);
  170.         TextSize(listFontSize);
  171.         GetFontInfo(&info);
  172.         listFontHeight = info.ascent + info.descent + info.leading;
  173.         /*
  174.          * Compute the list drawing area, adjusting the list area height so an
  175.          * integral number of lines will be drawn. As with the standard list
  176.          * manager, the scroll bars will be drawn outside of the list rectangle.
  177.          * Note that we do not normalize the list height if there is a grow box
  178.          * or if the bottom edge of the list rectangle coincides with the bottom
  179.          * edge of the window.
  180.          */
  181.         listRect = *viewRect;
  182.         if (hasGrowBox == FALSE && listRect.bottom < qd.thePort->portRect.bottom) {
  183.             listHeight = height(listRect);
  184.             listHeight -= (listHeight % listFontHeight);
  185.             listRect.bottom = listRect.top + listHeight;
  186.         }
  187.         SetPt(&cellSize, width(listRect), listFontHeight);
  188.         /*
  189.          * Note: we create a one-colum list with both vertical and horizontal
  190.          * scrollbars, then we steal the horizontal scrollbar because we're
  191.          * scrolling within the list cell. Unfortunately, the List Manager only
  192.          * scrolls from one cell (column in the horizontal direction) to another.
  193.          */
  194.         SetRect(&dataBounds, 0, 0, 1, 0);
  195.         logListHandle = LNew(
  196.                 &listRect,                            /* Viewing area                */
  197.                 &dataBounds,                        /* Rows and col's            */
  198.                 cellSize,                            /* Element size                */
  199.                 0,                                    /* No defProc yet            */
  200.                 qd.thePort,                            /* Display window            */
  201.                 TRUE,                                /* Draw it                    */
  202.                 hasGrowBox,                            /* No grow box                */
  203.                 TRUE,                                /* Has horizontal scroll    */
  204.                 TRUE                                /* Has vertical scroll        */
  205.             );
  206.         if (logListHandle == NULL)
  207.             goto failure;
  208.         LIST.selFlags = lOnlyOne;
  209.         //** LIST.listFlags = lDoVAutoscroll;    No autoscroll        /* Vertical autoscroll only    */
  210.         logInfo.logFileRefNum = 0;
  211.         logInfo.logFileVRefNum = 0;
  212.         logInfo.logFileStatus = 0;
  213.         logInfo.logLines = logLines;
  214.         logInfo.fontNumber = listFontNumber;
  215.         logInfo.fontSize = listFontSize;
  216.         if (COLOR_LIST) {
  217.             GetForeColor(&logInfo.foreColor);
  218.             GetBackColor(&logInfo.backColor);
  219.         }
  220.         status = PtrToHand(&logInfo, &logInfoHdl, sizeof logInfo);
  221.         if (status != noErr)
  222.             goto failure;
  223.         LIST.userHandle = (Handle) logInfoHdl;
  224.         HSCROLL = LIST.hScroll;                        /* Grab horizontal scroller    */
  225.         LIST.hScroll = NULL;                        /* Remove it from the list    */
  226.         SetCRefCon(HSCROLL, (long) logListHandle);    /* Link scrollbar to list    */
  227.         status = PtrToHand(gDummyLDEF, &drawProcHdl, sizeof gDummyLDEF);
  228.         if (status != noErr)
  229.             goto failure;
  230.         listProcPtr = (ProcPtr) LogListDefProc;
  231.         BlockMove(&listProcPtr, &((short *) *drawProcHdl)[3], sizeof listProcPtr);
  232.         LIST.listDefProc = drawProcHdl;
  233.         horizontalMax = kMaxHorizontalScroll - width((**HSCROLL).contrlRect);
  234.         if (horizontalMax < 0)
  235.             horizontalMax = 0;
  236.         SetCtlMin(HSCROLL, 0);
  237.         SetCtlMax(HSCROLL, horizontalMax);
  238.         SetCtlValue(HSCROLL, 0);
  239.         HiliteControl(
  240.             HSCROLL,
  241.             (horizontalMax == 0) ? kDisabledControl : kActiveControl
  242.         );
  243.         goto success;
  244. failure:
  245.         if (drawProcHdl != NULL) {
  246.             DisposeHandle((Handle) drawProcHdl);
  247.             LIST.listDefProc = NULL;
  248.         }
  249.         DisposeLog(logListHandle);
  250.         logListHandle = NULL;
  251. success:
  252.         return (logListHandle);
  253. }
  254.  
  255. /*
  256.  * DisposeLog disposes of our private information and then disposes of the list.
  257.  */
  258. void
  259. DisposeLog(
  260.         ListHandle                        logListHandle
  261.     )
  262. {
  263.         if (logListHandle != NULL) {
  264.             if (LIST.userHandle != NULL) {
  265.                 LIST.hScroll = HSCROLL;            /* The list manager disposes    */ 
  266.                 DisposeHandle(LIST.userHandle);
  267.                 LIST.userHandle = NULL;
  268.             }
  269.             LDispose(logListHandle);
  270.         }
  271. }
  272.  
  273. /*
  274.  * UpdateLogWindow explicitly updates the log's window. It is generally called only
  275.  * after Modal Dialogs or Alerts obscured the window.
  276.  */
  277. void
  278. UpdateLogWindow(
  279.         ListHandle                        logListHandle
  280.     )
  281. {
  282.         WindowPtr                        theWindow;
  283.         GrafPtr                            savePort;
  284.         Rect                            viewRect;
  285.         RgnHandle                        listRgn;
  286.         RgnHandle                        clipRgn;
  287.         
  288.         if (logListHandle != NULL) {
  289.             theWindow = (WindowPtr) LIST.port;
  290.             if (EmptyRgn(((WindowPeek) theWindow)->updateRgn) == FALSE) {
  291.                 viewRect = LIST.rView;
  292.                 if (LIST.hScroll != NULL)
  293.                     viewRect.bottom += kScrollBarWidth;
  294.                 if (LIST.hScroll != NULL)
  295.                     viewRect.right += kScrollBarWidth;
  296.                 listRgn = NewRgn();
  297.                 RectRgn(listRgn, &viewRect);
  298.                 SectRgn(listRgn, ((WindowPeek) theWindow)->updateRgn, listRgn);
  299.                 if (EmptyRgn(listRgn) == FALSE) {
  300.                     /*
  301.                      * We have something to redraw. Fake an update event handler.
  302.                      */
  303.                     GetPort(&savePort);
  304.                     SetPort(theWindow);
  305.                     clipRgn = NewRgn();
  306.                     GetClip(clipRgn);
  307.                     SetClip(listRgn);
  308.                     EraseRgn(listRgn);
  309.                     UpdateLog(logListHandle);
  310.                     SetClip(clipRgn);
  311.                     DisposeRgn(clipRgn);
  312.                     ValidRgn(listRgn);
  313.                     SetPort(savePort);
  314.                 }
  315.                 DisposeRgn(listRgn);
  316.             }
  317.         }
  318. }
  319.  
  320. /*
  321.  * UpdateLog redraws the list.
  322.  */
  323. void
  324. UpdateLog(
  325.         ListHandle                        logListHandle
  326.     )
  327. {
  328.         Rect                        viewRect;
  329.         RGBColor                    saveForeColor;
  330.         RGBColor                    saveBackColor;
  331.         
  332.         if (logListHandle != NULL) {
  333.             /*
  334.              * Make sure the list is locked down while we draw. Note that we
  335.              * assume that the application has called UpdateControls, so
  336.              * the horizontal scrollbar is correctly drawn.
  337.              */        
  338.             if (COLOR_LIST) {
  339.                 GetForeColor(&saveForeColor);
  340.                 GetBackColor(&saveBackColor);
  341.                 RGBForeColor(&LOGINFO.foreColor);
  342.                 RGBBackColor(&LOGINFO.backColor);
  343.             }
  344.             UnionRect(
  345.                 &(**LIST.vScroll).contrlRect,
  346.                 &(**HSCROLL).contrlRect,
  347.                 &viewRect
  348.             );
  349.             FrameRect(&viewRect);
  350.             LUpdate(LIST.port->visRgn, logListHandle);
  351.             if (COLOR_LIST) {
  352.                 RGBForeColor(&saveForeColor);
  353.                 RGBBackColor(&saveBackColor);
  354.             }
  355.         }
  356. }
  357.  
  358. /*
  359.  * ActivateLog activates (or deactivates) the log. Call it on activate and
  360.  * suspendResume events.
  361.  */
  362. void
  363. ActivateLog(
  364.         ListHandle                        logListHandle,
  365.         Boolean                            activating
  366.     )
  367. {
  368.         if (logListHandle != NULL) {
  369.             LActivate(activating, logListHandle);
  370.             HiliteControl(
  371.                 HSCROLL,
  372.                 (activating) ? kActiveControl : kDisabledControl);
  373.         }
  374. }
  375.  
  376. /*
  377.  * MoveLog Repositions the log list area.
  378.  */
  379. void
  380. MoveLog(
  381.         ListHandle                        logListHandle,
  382.         short                            leftEdge,
  383.         short                            topEdge
  384.     )
  385. {
  386.         Rect                            viewRect;
  387.         
  388.         if (logListHandle != NULL) {
  389.             if (LIST.rView.left != leftEdge || LIST.rView.top != topEdge) {
  390.                 viewRect = LIST.rView;
  391.                 InsetRect(&viewRect, -1, -1);
  392.                 InvalRect(&viewRect);
  393.                 OffsetRect(
  394.                     &LIST.rView,
  395.                     leftEdge - LIST.rView.left,
  396.                     topEdge -LIST.rView.top
  397.                 );
  398.                 viewRect = LIST.rView;
  399.                 InsetRect(&viewRect, -1, -1);
  400.                 InvalRect(&viewRect);
  401.                 MoveControl(
  402.                     LIST.vScroll,
  403.                     LIST.rView.right - kScrollBarOffset,
  404.                     LIST.rView.top - 1
  405.                 );
  406.                 MoveControl(
  407.                     HSCROLL,
  408.                     LIST.rView.left - 1,
  409.                     LIST.rView.bottom - kScrollBarOffset
  410.                 );
  411.             }
  412.         }
  413. }
  414.  
  415. /*
  416.  * SizeLog: this is the list rectangle size and does not include the scrollbars.
  417.  */
  418. void
  419. SizeLog(
  420.         ListHandle                        logListHandle,
  421.         short                            newWidth,
  422.         short                            newHeight
  423.     )
  424. {
  425.         Rect                            viewRect;
  426.         Point                            cellSize;
  427.         short                            horizontalMax;
  428.         
  429.         if (logListHandle != NULL) {
  430.             viewRect = LIST.rView;
  431.             InsetRect(&viewRect, -1, -1);
  432.             InvalRect(&viewRect);
  433.             /*
  434.              * We put the horizontal scrollbar back into the list record so that
  435.              * the list manager kindly resizes it. Then we take it out again.
  436.              */
  437.             LIST.hScroll = HSCROLL;
  438.             LSize(newWidth, newHeight, logListHandle);
  439.             LIST.hScroll = NULL;
  440.             cellSize = LIST.cellSize;
  441.             cellSize.h = width(LIST.rView);
  442.             LCellSize(cellSize, logListHandle);
  443.             horizontalMax = kMaxHorizontalScroll - width((**HSCROLL).contrlRect);
  444.             if (horizontalMax < 0)
  445.                 horizontalMax = 0;
  446.             SetCtlMax(HSCROLL, horizontalMax);
  447.             HiliteControl(
  448.                 HSCROLL,
  449.                 (horizontalMax == 0) ? kDisabledControl : kActiveControl
  450.             );
  451.             UnionRect(
  452.                 &(**LIST.vScroll).contrlRect,
  453.                 &(**HSCROLL).contrlRect,
  454.                 &viewRect
  455.             );
  456.             InvalRect(&viewRect);
  457.         }
  458. }
  459.  
  460. /*
  461.  * DoClickInLog: call this when there is a mouse down in the log area (or in one
  462.  * of the scrollbars. TRUE if we handled this event (the click was in the log).
  463.  * The LClick result is not returned.
  464.  */
  465. Boolean
  466. DoClickInLog(
  467.         ListHandle                        logListHandle,
  468.         const EventRecord                *eventRecord
  469.     )
  470. {
  471. #define EVENT    (*eventRecord)
  472.  
  473.         Point                            mousePt;
  474.         Boolean                            result;
  475.         Rect                            viewRect;
  476.         short                            part;
  477.         ControlHandle                    theControl;
  478.  
  479.         if (logListHandle == NULL)
  480.             result = FALSE;
  481.         else {
  482.             mousePt = EVENT.where;
  483.             GlobalToLocal(&mousePt);
  484.             /*
  485.              * Handle clicks in the horizontal scrollbar: Do not pass them through
  486.              * LClick, as it does not do what we want and besides, the scrollbar
  487.              * isn't there any more.
  488.              */
  489.             if (PtInRect(mousePt, &(**HSCROLL).contrlRect)) {
  490.                 part = FindControl(mousePt, (**HSCROLL).contrlOwner, &theControl);
  491.                 if (part >= 0 && theControl == HSCROLL) {
  492.                     if (part == inThumb) {
  493.                         if (TrackControl(theControl, mousePt, NULL))
  494.                             ScrollLogList(theControl);
  495.                     }
  496.                     else {
  497.                         TrackControl(theControl, mousePt, (ProcPtr) ScrollLogAction);
  498.                     }
  499.                 }
  500.                 result = TRUE;
  501.             }
  502.             else {
  503.                 viewRect = LIST.rView;
  504.                 viewRect.right += kScrollBarOffset;
  505.                 if ((result = PtInRect(mousePt, &viewRect)))
  506.                     (void) LClick(mousePt, EVENT.modifiers, logListHandle);
  507.             }
  508.         }
  509.         return (result);
  510. #undef EVENT
  511. }
  512.  
  513. /*
  514.  * Log errors.
  515.  */
  516. void
  517. LogStatus(
  518.         ListHandle                        logListHandle,
  519.         OSErr                            theError,
  520.         const StringPtr                    infoText
  521.     )
  522. {
  523.         Handle                            macErrorHdl;
  524.         Str255                            msg;
  525.         Str15                            errorValue;
  526.         
  527.         if (logListHandle != NULL && theError != noErr) {
  528.             pstrcpy(msg, infoText);
  529.             pstrcat(msg, "\p: ");
  530.             NumToString(theError, errorValue);
  531.             pstrcat(msg, errorValue);
  532.             macErrorHdl = GetResource('Estr', theError);
  533.             if (macErrorHdl != NULL) {
  534.                 pstrcat(msg, "\p ");
  535.                 pstrcat(msg, (StringPtr) *macErrorHdl);
  536.                 ReleaseResource(macErrorHdl);
  537.             }
  538.             DisplayLogString(logListHandle, msg);
  539.         }
  540. }
  541.  
  542. /*
  543.  * DisplayLogString
  544.  * Call this function to store a string in the list.
  545.  */
  546. void
  547. DisplayLogString(
  548.         ListHandle                        logListHandle,
  549.         const StringPtr                    theString
  550.     )
  551. {
  552.  
  553.         Cell                        theCell;
  554.         short                        theRow;
  555.         Boolean                        scrollAtBottom;
  556.         
  557.         if (logListHandle != NULL) {
  558.             /*
  559.              * If there are already logLines in the
  560.              * list, delete the first row of the list.
  561.              * Then, in any case, append this datum at the
  562.              * bottom.
  563.              *
  564.              * The scroll bars are managed as follows:
  565.              * scroll bar is at the bottom, the new datum
  566.              * is selected and autoscrolled into view.
  567.              * Otherwise, the current cell is unchanged.
  568.              */
  569.             theRow = LIST.dataBounds.bottom;
  570.             scrollAtBottom =
  571.                 (GetCtlValue(LIST.vScroll) == GetCtlMax(LIST.vScroll));
  572.             if (theRow >= LOGINFO.logLines)
  573.                 LDelRow(1, 0, logListHandle);
  574.             if (AddStringToList(logListHandle, theString) == noErr) {
  575.                 if (scrollAtBottom) {
  576.                     theCell.v = 0;
  577.                     if (LGetSelect(TRUE, &theCell, logListHandle))
  578.                         LSetSelect(FALSE, theCell, logListHandle);
  579.                     theCell.v = theRow;
  580.                     LSetSelect(TRUE, theCell, logListHandle);
  581.                     LDoDraw(TRUE, logListHandle);
  582.                     LAutoScroll(logListHandle);
  583.                 }
  584.                 LDraw(theCell, logListHandle);
  585.                 if (LOGINFO.logFileRefNum != 0 && LOGINFO.logFileStatus == noErr)
  586.                     WriteLogLine(logListHandle, theString);
  587.             }
  588.         }
  589. failure:
  590.         return;
  591. }
  592.  
  593. static OSErr
  594. AddStringToList(
  595.         ListHandle                        logListHandle,
  596.         ConstStr255Param                theString
  597.     )
  598. {
  599.         StringHandle                    stringHandle;
  600.         Cell                            theCell;
  601.         
  602.         stringHandle = NewString(theString);
  603.         if (stringHandle != NULL) {
  604.             theCell.h = 0;
  605.             theCell.v = LAddRow(1, LIST.dataBounds.bottom, logListHandle);
  606.             if (MemError() == noErr)
  607.                 LSetCell(&stringHandle, sizeof stringHandle, theCell, logListHandle);
  608.         }
  609.         return (MemError());
  610. }
  611.  
  612. /*
  613.  * ScrollLogAction is called by the Toolbox while executing the TrackControl
  614.  * routine.  It has to take care of scrolling the log when the user clicks on the
  615.  * up/down arrow or page parts of the scroll bar.
  616.  */
  617. static pascal void
  618. ScrollLogAction(
  619.         register ControlHandle            theControl,
  620.         short                            partcode
  621.     )
  622. {
  623.         short                            delta;
  624.         
  625.         delta = (width((**theControl).contrlRect) * 7) / 8;
  626.         switch (partcode) {
  627.         case inUpButton:    delta = -CharWidth('M');    break;
  628.         case inPageUp:        delta = -(delta);            break;        
  629.         case inDownButton:    delta = CharWidth('M');        break;
  630.         case inPageDown:    /* All set */                break;
  631.         default:            return;        /* Mouse exited control    */
  632.         }
  633.         SetCtlValue(theControl, GetCtlValue(theControl) + delta);
  634.         ScrollLogList(theControl);
  635. }
  636.  
  637. /*
  638.  * ScrollLogList scrolls the list rectangle in the proper direction and updates
  639.  * the list's horizontal indentation to match.
  640.  */
  641. static void
  642. ScrollLogList(
  643.         ControlHandle                    theControl
  644.     )
  645. {
  646.         ListHandle                        logListHandle;
  647.         short                            delta;
  648.         RgnHandle                        clipRgn;
  649.         RgnHandle                        updateRgn;
  650.         Rect                            viewRect;
  651.         
  652.         logListHandle = (ListHandle) GetCRefCon(theControl);
  653.         /*
  654.          * LIST.indent.h is negative when the cell is scrolled left. Get the
  655.          * amount it's currently scrolled (as a positive value) and set delta
  656.          * to the amount that must be scrolled. Delta will be positive to
  657.          * scroll right (which means that the scrollbar has moved left).
  658.          */
  659.         delta = kZeroIndent - LIST.indent.h - GetCtlValue(theControl);
  660.         if (delta != 0) {
  661.             /*
  662.              * We need to scroll the list cells. Get a clip rectangle so the
  663.              * scrolling is limited to the drawing area, scroll it, and update
  664.              * the stuff that came into view.
  665.              */
  666.             viewRect = LIST.rView;
  667.             clipRgn = NewRgn();
  668.             updateRgn = NewRgn();
  669.             GetClip(clipRgn);
  670.             ClipRect(&viewRect);
  671.             ScrollRect(&viewRect, delta, 0, updateRgn);
  672.             LIST.indent.h += delta;
  673.             LUpdate(updateRgn, logListHandle);
  674.             SetClip(clipRgn);
  675.             DisposeRgn(updateRgn);
  676.             DisposeRgn(clipRgn);
  677.         }
  678. }
  679.         
  680. /*
  681.  * Draw the string stored in the list. The only difference between this function
  682.  * and a "normal" LDEF is that we don't visually indicate selection.
  683.  */
  684. static pascal void
  685. LogListDefProc(
  686.         short                            listMessage,
  687.         Boolean                            listSelect,
  688.         Rect                            *listRect,
  689.         Cell                            listCell,
  690.         short                            listDataOffset,
  691.         short                            listDataLen,
  692.         ListHandle                        logListHandle
  693.     )
  694. {
  695. #pragma unused (listCell)
  696.  
  697.         char                            stringLockState;
  698.         RGBColor                        saveForeColor;
  699.         RGBColor                        saveBackColor;
  700.         StringHandle                    stringHandle;
  701.         
  702.         /*
  703.          * If the userHandle isn't setup, do nothing: this is an initialization
  704.          * or a spurious command while we're disposing of the list.
  705.          */
  706.         if (LIST.userHandle != NULL) {
  707.             TextFont(LOGINFO.fontNumber);
  708.             TextSize(LOGINFO.fontSize);
  709.             if (COLOR_LIST) {
  710.                 GetForeColor(&saveForeColor);
  711.                 GetBackColor(&saveBackColor);
  712.                 RGBForeColor(&LOGINFO.foreColor);
  713.                 RGBBackColor(&LOGINFO.backColor);
  714.             }
  715.             switch (listMessage) {
  716.             case lInitMsg:
  717.                 break;
  718.             case lDrawMsg:
  719.                 EraseRect(listRect);
  720.                 if (listDataLen == sizeof stringHandle) {
  721.                     BlockMove(
  722.                         (*LIST.cells) + listDataOffset,
  723.                         &stringHandle,
  724.                         sizeof stringHandle
  725.                     );
  726.                     /*
  727.                      * We don't indent in the vertical direction: by default,
  728.                      * it contains the font ascent which is fine for DrawText
  729.                      */
  730.                     stringLockState = HGetState((Handle) stringHandle);
  731.                     HLock((Handle) stringHandle);
  732.                     MoveTo(
  733.                         listRect->left + LIST.indent.h,
  734.                         listRect->top + LIST.indent.v
  735.                     );
  736.                     DrawString(*stringHandle);
  737.                     HSetState((Handle) stringHandle, stringLockState);
  738.                 }
  739.                 if (listSelect == FALSE)
  740.                     break;
  741.                 /* Continue to do hilite */
  742.             case lHiliteMsg:
  743. #if 0        /* Hiliting is disabled */
  744. #ifdef THINK_C
  745.                 HiliteMode &= ~(1 << hiliteBit);
  746. #else /* MPW */
  747.                 *((char *) HiliteMode) &= ~(1 << hiliteBit); /* Inside Mac V-61    */
  748. #endif
  749.                 InvertRect(listRect);
  750. #endif
  751.                 break;
  752.             }
  753.             if (COLOR_LIST) {
  754.                 RGBForeColor(&saveForeColor);
  755.                 RGBBackColor(&saveBackColor);
  756.             }
  757.         }
  758. }
  759.  
  760. StringHandle
  761. GetLogStringHandle(
  762.         ListHandle                    logListHandle,
  763.         short                        theRow
  764.     )
  765. {
  766.         Cell                        theCell;
  767.         StringHandle                result;
  768.         short                        dataLength;
  769.         
  770.         dataLength = sizeof result;
  771.         theCell.h = 0;
  772.         theCell.v = theRow;
  773.         LGetCell(&result, &dataLength, theCell, logListHandle);
  774.         if (dataLength != sizeof result)
  775.             result = NULL;
  776.         return (result);
  777. }
  778.  
  779. /*
  780.  * Prompt the user for a file name and write the current log contents
  781.  * to the chosen file. Returns an error status (noErr is ok). This function
  782.  * returns userCanceledErr if the user cancelled the SFPutFile dialog.
  783.  */
  784. OSErr
  785. SaveLogFile(
  786.         ListHandle                        logListHandle,
  787.          ConstStr255Param                dialogPromptString,
  788.         ConstStr255Param                defaultFileName,
  789.         OSType                            creatorType
  790.     )
  791. {
  792.         Point                            where;
  793.         SFReply                            reply;
  794.         DialogTHndl                        dialog;
  795.         Rect                            box;
  796.         OSErr                            status;
  797.         
  798.         /*
  799.          * Center the dialog
  800.          */
  801.         dialog = (DialogTHndl) GetResource('DLOG', putDlgID);
  802.         if (dialog == NULL)                             /* No such dialog!    */
  803.             SetRect(&box, 0, 0, 0, 0);                /* Center on screen    */
  804.         else {
  805.             box = (*dialog)->boundsRect;             /* Dialog shape        */
  806.             ReleaseResource((Handle) dialog);        /* Done with dialog    */
  807.         }
  808.         SetPt(&where,
  809.             (width(qd.thePort->portRect) - width(box)) / 2,
  810.             ((height(qd.thePort->portRect) - GetMBarHeight()) / 3)
  811.                 + GetMBarHeight()
  812.         );
  813.         SFPutFile(where, dialogPromptString, defaultFileName, NULL, &reply);
  814.         if (reply.good == FALSE)
  815.             status = userCanceledErr;
  816.         else {
  817.             SetCursor(*GetCursor(watchCursor));
  818.             status = CreateLogFile(
  819.                         logListHandle,
  820.                         creatorType,
  821.                         reply.fName,
  822.                         reply.vRefNum
  823.                     );
  824.             InitCursor();
  825.             if (status != noErr)
  826.                 (void) FSDelete(reply.fName, reply.vRefNum);
  827.         }
  828.         return (status);
  829. }
  830.  
  831. /*
  832.  * Create a log on the specified file and volume. Normally, called only
  833.  * by SaveLogFile.
  834.  */
  835. OSErr
  836. CreateLogFile(
  837.         ListHandle                        logListHandle,
  838.         OSType                            creatorType,
  839.         ConstStr255Param                fileName,
  840.         short                            vRefNum
  841.     )
  842. {
  843.         OSErr                            status;
  844.         short                            refNum;
  845.         
  846.         /*
  847.          * Create the file, elmininating any duplicate.
  848.          */
  849.         if (creatorType == 0)
  850.             creatorType = 'ttxt';                    /* TeachText            */
  851.         status = Create(fileName, vRefNum, creatorType, 'TEXT');
  852.         if (status == dupFNErr) {                    /* Exists already?        */
  853.             status = FSDelete(fileName, vRefNum);
  854.             if (status == noErr)
  855.                 status = Create(fileName, vRefNum, creatorType, 'TEXT');
  856.         }
  857.         if (status == noErr)
  858.             status = FSOpen(fileName, vRefNum, &refNum);
  859.         if (status == noErr) {
  860.             LOGINFO.logFileRefNum = refNum;
  861.             LOGINFO.logFileVRefNum = vRefNum;
  862.             LOGINFO.logFileStatus = noErr;
  863.             WriteCurrentLog(logListHandle);
  864.             status = LOGINFO.logFileStatus;
  865.         }
  866.         if (status != noErr) {
  867.             (void) CloseLogFile(logListHandle);
  868.             LogStatus(logListHandle, status, "\pCreateLogFile failed");
  869.         }
  870.         return (status);
  871. }
  872.  
  873. /*
  874.  * Write the current contents of the log file. Any error will be
  875.  * in LOGINFO.logFileStatus.
  876.  */
  877. void
  878. WriteCurrentLog(
  879.         ListHandle                        logListHandle
  880.     )
  881. {
  882.         short                            theRow;
  883.         long                            fileLength;
  884.         StringHandle                    stringHandle;
  885.         char                            stringLockState;
  886.  
  887.         
  888.         theRow = LIST.dataBounds.top;
  889.         while (LOGINFO.logFileStatus == noErr && theRow < LIST.dataBounds.bottom) {
  890.             stringHandle = GetLogStringHandle(logListHandle, theRow);
  891.             if (stringHandle != NULL) {
  892.                 stringLockState = HGetState((Handle) stringHandle);
  893.                 HLock((Handle) stringHandle);
  894.                 fileLength = (*stringHandle)[0];
  895.                 LOGINFO.logFileStatus = FSWrite(
  896.                     LOGINFO.logFileRefNum,
  897.                     &fileLength,
  898.                     &(*stringHandle)[1]
  899.                 );
  900.                 HSetState((Handle) stringHandle, stringLockState);
  901.                 if (LOGINFO.logFileStatus == noErr) {
  902.                     fileLength = 1;
  903.                     LOGINFO.logFileStatus = FSWrite(
  904.                                 LOGINFO.logFileRefNum,
  905.                                 &fileLength,
  906.                                 endOfLine
  907.                             );
  908.                 }
  909.             }
  910.             ++theRow;
  911.         };
  912. }        
  913.  
  914. void
  915. WriteLogLine(
  916.         ListHandle                        logListHandle,
  917.         ConstStr255Param                theText
  918.     )
  919. {
  920.         long                            fileLength;
  921.         
  922.         if (LOGINFO.logFileRefNum != 0 && LOGINFO.logFileStatus == noErr) {
  923.             fileLength = theText[0];
  924.             LOGINFO.logFileStatus = FSWrite(
  925.                         LOGINFO.logFileRefNum,
  926.                         &fileLength,
  927.                         &theText[1]
  928.                     );
  929.             if (LOGINFO.logFileStatus == noErr) {
  930.                 fileLength = 1;
  931.                 LOGINFO.logFileStatus = FSWrite(
  932.                             LOGINFO.logFileRefNum,
  933.                             &fileLength,
  934.                             endOfLine
  935.                         );
  936.             }
  937.             if (LOGINFO.logFileStatus != noErr) {
  938.                 CloseLogFile(logListHandle);
  939.                 LogStatus(
  940.                     logListHandle,
  941.                     LOGINFO.logFileStatus,
  942.                     "\pCan't write to log file"
  943.                 );
  944.             }
  945.         }
  946. }
  947.  
  948. OSErr
  949. CloseLogFile(
  950.         ListHandle                        logListHandle
  951.     )
  952. {
  953.         OSErr                            status;
  954.         
  955.         status = noErr;
  956.         if (LOGINFO.logFileRefNum != 0) {
  957.             status = FSClose(LOGINFO.logFileRefNum);
  958.             if (status == noErr)
  959.                 status = FlushVol(NULL, LOGINFO.logFileVRefNum);
  960.         }
  961.         LOGINFO.logFileRefNum = 0;
  962.         LOGINFO.logFileVRefNum = 0;
  963.         if (status != noErr)
  964.             LogStatus(logListHandle, status, "\pCan't close log file");
  965.         if (LOGINFO.logFileStatus == noErr)
  966.             LOGINFO.logFileStatus = status;
  967.         status = LOGINFO.logFileStatus;
  968.         LOGINFO.logFileStatus = noErr;
  969.         return (status);
  970. }
  971.  
  972. OSErr
  973. GetLogFileError(
  974.         ListHandle                        logListHandle
  975.     )
  976. {
  977.         return (LOGINFO.logFileStatus);
  978. }
  979.  
  980. Boolean
  981. HasLogFile(
  982.         ListHandle                        logListHandle
  983.     )
  984. {
  985.         return (LOGINFO.logFileRefNum != 0);
  986. }
  987.  
  988. /*
  989.  * Copyright © 1993, Apple Computer Inc. All Rights reserved.
  990.  *
  991.  * This started life as a generic print driver written by Rich Siegel and posted
  992.  * to Usenet in around 1988 or so. It has been extensively rewritten, but not
  993.  * necessarily improved.
  994.  */
  995.  
  996. /*
  997.  *        OSErr
  998.  *        PrintDriver(
  999.  *                THPrint            hPrint,
  1000.  *                void            *clientData,
  1001.  *                Boolean            doStyle,
  1002.  *                OSErr            (*userPrepProc)(
  1003.  *                                        THPrnt        hPrint,
  1004.  *                                        void        *clientData
  1005.  *                                    ),
  1006.  *                OSErr            (*userPageProc)(
  1007.  *                                        THPrnt        hPrint,
  1008.  *                                        void        *clientData,
  1009.  *                                        const Rect    *pageRect,
  1010.  *                                        short        pageNum
  1011.  *                                    )
  1012.  *            );
  1013.  *        hPrint        A Print Manager handle.  If NULL, ReportPrintDriver
  1014.  *                    will allocate a handle, call the Page Setup dialog,
  1015.  *                    and dispose of the handle on exit.
  1016.  *        clientData    A longword parameter that becomes a parameter to the
  1017.  *                    prepProc and pageProc functions. For the LogManager,
  1018.  *                    it is the ListHandle.
  1019.  *        doStyle        If TRUE, the page setup modal dialog is always called.
  1020.  *                    Otherwise, it's called only if the print record is invalid.
  1021.  *
  1022.  *        userPrepProc A user-provided function that will perform print setup
  1023.  *                    initializations.  It must be defined
  1024.  *                        OSErr            UserPrepProc(
  1025.  *                                THPrint        hPrint,
  1026.  *                                void        *clientData
  1027.  *                            );
  1028.  *                    UserPrepProc returns noErr on success or an error status that
  1029.  *                    will  be returned to the PrintDriver caller on failure.
  1030.  *                    UserPrepProc must set (**hPrint).prJob.iLstPage  to the correct
  1031.  *                    number of pages in the document. If all pages are specified
  1032.  *                    by the user dialog, this field will be 999 when UserPrepProc
  1033.  *                    is called; it must then be re-set to the correct value.
  1034.  *        userPageProc A user-provided function that draws the page.  It is defined:
  1035.  *                        OSErr            UserPageProc(
  1036.  *                                THPrint        hPrint,
  1037.  *                                void        *clientData,
  1038.  *                                Rect        *pageRect,
  1039.  *                                short        pageNum
  1040.  *                        );
  1041.  *                    UserPageProc returns noErr if it completed correctly, or an
  1042.  *                    error code that will be returned to the user.  It should
  1043.  *                    return iPrAbort on user-specified aborts.
  1044.  */
  1045.  
  1046. /*
  1047.  * Note: the serial stuff has not been tested in several years.
  1048.  */
  1049. #ifndef bDevCItoh
  1050. #define bDevCItoh        1                /* ImageWriter                */
  1051. #endif
  1052. #ifndef bDevLaser
  1053. #define bDevLaser        3                /* LaserWriter                */
  1054. #endif
  1055. #define ImageWriter        (bDevCItoh)
  1056. #define    LaserWriter        (bDevLaser)
  1057.  
  1058. OSErr                                LogPrepProc(
  1059.         THPrint                            hPrint,
  1060.         short                            fontNumber,
  1061.         short                            fontSize,
  1062.         void                            *clientData
  1063.     );
  1064. OSErr                                LogPageProc(
  1065.         THPrint                            hPrint,
  1066.         short                            fontNumber,
  1067.         short                            fontSize,
  1068.         void                            *clientData,
  1069.         const Rect                        *pageRect,
  1070.         short                            pageNumber
  1071.     );
  1072. OSErr                                PrintDriver(
  1073.         THPrint                            hPrint,
  1074.         short                            fontNumber,
  1075.         short                            fontSize,
  1076.         Boolean                            doStyleDialog,
  1077.         void                            *clientData,
  1078.         OSErr                            (*userPrepProc)(
  1079.                         THPrint        hPrint,
  1080.                         short        fontNumber,
  1081.                         short        fontSize,
  1082.                         void        *clientData
  1083.                     ),
  1084.         OSErr                            (*userPageProc)(
  1085.                         THPrint        hPrint,
  1086.                         short        fontNumber,
  1087.                         short        fontSize,
  1088.                         void        *clientData,
  1089.                         const Rect    *pageRect,
  1090.                         short        pageNumber
  1091.                     )
  1092.     );
  1093.  
  1094. /*
  1095.  * PrintLog, LogPrepProc, and LogPageProc are specific to the LogManager.
  1096.  */
  1097.  
  1098. OSErr
  1099. PrintLog(
  1100.         ListHandle                        logListHandle,
  1101.         THPrint                            hPrint,
  1102.         short                            fontNumber,
  1103.         short                            fontSize
  1104.     )
  1105. {
  1106.         OSErr            status;
  1107.         
  1108.         status = PrintDriver(
  1109.                     hPrint,
  1110.                     fontNumber,
  1111.                     fontSize,
  1112.                     FALSE,
  1113.                     logListHandle,
  1114.                     LogPrepProc,
  1115.                     LogPageProc
  1116.                 );
  1117.         return (status);
  1118. }
  1119.  
  1120. OSErr
  1121. LogPrepProc(
  1122.         THPrint                        hPrint,
  1123.         short                        fontNumber,
  1124.         short                        fontSize,
  1125.         void                        *clientData
  1126.     )
  1127. {
  1128.         unsigned short                linesInLog;
  1129.         unsigned short                linesPerPage;
  1130.         unsigned short                lineHeight;
  1131.         unsigned short                pagesInLog;
  1132.         Rect                        printRect;
  1133.         FontInfo                    info;
  1134. #define logListHandle    ((ListHandle) clientData)
  1135.  
  1136.         /*
  1137.          * We could add a page header here, of course.
  1138.          */
  1139.         printRect = (**hPrint).prInfo.rPage;
  1140.         TextFont(fontNumber);
  1141.         TextSize(fontSize);
  1142.         GetFontInfo(&info);
  1143.         lineHeight = info.ascent + info.descent + info.leading;
  1144.         linesInLog = height(LIST.dataBounds);
  1145.         linesPerPage = height(printRect) / lineHeight;
  1146.         pagesInLog = (linesInLog + linesPerPage - 1) / linesPerPage;
  1147.         (**hPrint).prJob.iLstPage = pagesInLog;
  1148.         return (noErr);
  1149. #undef logListHandle
  1150. }
  1151.  
  1152. OSErr
  1153. LogPageProc(
  1154.         THPrint                            hPrint,
  1155.         short                            fontNumber,
  1156.         short                            fontSize,
  1157.         void                            *clientData,
  1158.         const Rect                        *pageRect,
  1159.         short                            pageNumber
  1160.     )
  1161. {
  1162. #pragma unused (hPrint)
  1163.         unsigned short                    linesPerPage;
  1164.         short                            lastCell;
  1165.         Point                            drawLoc;
  1166.         FontInfo                        info;
  1167.         unsigned short                    lineHeight;
  1168.         short                            theRow;
  1169.         StringHandle                    stringHandle;
  1170.         char                            stringLockState;
  1171. #define logListHandle    ((ListHandle) clientData)
  1172.  
  1173.         linesPerPage = height(*pageRect) / LIST.cellSize.v;
  1174.         TextFont(fontNumber);
  1175.         TextSize(fontSize);
  1176.         GetFontInfo(&info);
  1177.         lineHeight = info.ascent + info.descent + info.leading;
  1178.         theRow = (pageNumber - 1) * linesPerPage + LIST.dataBounds.top;
  1179.         lastCell = theRow + linesPerPage - 1;
  1180.         if (lastCell > LIST.dataBounds.bottom)
  1181.             lastCell = LIST.dataBounds.bottom;
  1182.         SetPt(
  1183.             &drawLoc,
  1184.             LIST.indent.h,
  1185.             pageRect->top + info.ascent
  1186.         );
  1187.         /*
  1188.          * We could add a page header here, of course.
  1189.          */
  1190.         for (; theRow < lastCell; ++theRow) {
  1191.             stringHandle = GetLogStringHandle(logListHandle, theRow);
  1192.             if (stringHandle != NULL) {
  1193.                 stringLockState = HGetState((Handle) stringHandle);
  1194.                 HLock((Handle) stringHandle);
  1195.                 MoveTo(drawLoc.h, drawLoc.v);
  1196.                 DrawString(*stringHandle);
  1197.                 HSetState((Handle) stringHandle, stringLockState);
  1198.             }
  1199.             drawLoc.v += lineHeight;
  1200.         };
  1201.         return (PrError());
  1202. }
  1203.  
  1204. /*
  1205.  * Rich Siegel's generic printer driver, somewhat modified.
  1206.  */
  1207. OSErr
  1208. PrintDriver(
  1209.         THPrint                            hPrint,
  1210.         short                            fontNumber,
  1211.         short                            fontSize,
  1212.         Boolean                            doStyleDialog,
  1213.         void                            *clientData,
  1214.         OSErr                            (*userPrepProc)(
  1215.                         THPrint        hPrint,
  1216.                         short        fontNumber,
  1217.                         short        fontSize,
  1218.                         void        *clientData
  1219.                     ),
  1220.         OSErr                            (*userPageProc)(
  1221.                         THPrint        hPrint,
  1222.                         short        fontNumber,
  1223.                         short        fontSize,
  1224.                         void        *clientData,
  1225.                         const Rect    *pageRect,
  1226.                         short        pageNumber
  1227.                     )
  1228.     )
  1229. {
  1230.         OSErr                status;                /* Random status                */
  1231.         Boolean                ourPrintHandle;        /* Did we allocate hPrint        */
  1232.         Boolean                printIsOpen;        /* PROpen-PRClose                 */        
  1233.         Boolean                docIsOpen;            /* PROpenDoc-PRCloseDoc            */
  1234.         Boolean                pageIsOpen;            /* PROpenPage-PRClosePage        */
  1235.         short                nCopies;            /* Number of copies             */
  1236.         short                printDevice;        /* What kind of printer            */
  1237.         Boolean                draftMode;            /* Draft or Spool?                */
  1238.         TPPrPort            printPort;            /* The print port                */
  1239.         TPrStatus            printStatus;        /* PrPicFile status info        */
  1240.         GrafPtr                savePort;            /* Old GrafPort                    */
  1241.         short                iCopy;                /* Which copy                    */
  1242.         short                page;                /* Which page                     */
  1243.         Rect                pageRect;            /* Current page image rect        */
  1244. /*
  1245.  * This macro exits the print handler on any error.
  1246.  */
  1247. #define    CheckError(s)    do {                \
  1248.         if ((status = (s)) != noErr)        \
  1249.             goto exit;                        \
  1250.     } while (0);
  1251.  
  1252.         GetPort(&savePort);
  1253.         status = noErr;
  1254.         printIsOpen = FALSE;
  1255.         docIsOpen = FALSE;
  1256.         pageIsOpen = FALSE;
  1257.         ourPrintHandle = (hPrint == NULL);
  1258.         PrOpen();
  1259.         CheckError(PrError());
  1260.         printIsOpen = TRUE;
  1261.         /*
  1262.          * Allocate the print handle if necessary.  memFullErr on failure
  1263.          * it failed.
  1264.          */
  1265.         if (ourPrintHandle) {
  1266.             hPrint = (THPrint) NewHandle(sizeof (TPrint));
  1267.             if (hPrint == NULL) {
  1268.                 status = MemError();
  1269.                 goto exit;
  1270.             }
  1271.             PrintDefault(hPrint);
  1272.         }
  1273.         /*
  1274.          * Validate the print handle and call the Style Dialog if necessary.  If
  1275.          * the user cancels, just exit (noErr). Then call the job dialog to get
  1276.          * the number of copies. Exit (noErr) if the user Cancels. (Strictly
  1277.          * speaking, we could exit with userCanceledErr, but that just forces
  1278.          * the caller to ignore this informational status.
  1279.          */
  1280.         if (PrValidate(hPrint) || doStyleDialog) {
  1281.             if (PrStlDialog(hPrint) == FALSE)
  1282.                 goto exit;
  1283.         }
  1284.         if (PrJobDialog(hPrint) == FALSE)
  1285.             goto exit;
  1286.         /*
  1287.          * Our setup is done. call the user's prep procedure and exit on errors.
  1288.          */
  1289.         SetCursor(*GetCursor(watchCursor));
  1290.         CheckError((*userPrepProc)(
  1291.             hPrint,
  1292.             fontNumber,
  1293.             fontSize,
  1294.             clientData
  1295.         ));
  1296.         /*
  1297.          * Grab a few interesting parameters:
  1298.          *    printDevice    Which printer we're using
  1299.          *    is_draftMode    TRUE if we're in draft-mode.
  1300.          *    nCopies            We do copies if we're in draft-mode on an Imagewriter.
  1301.          *                    Otherwise, the spooler does it for us. This hasn't
  1302.          *                    been tested in years.
  1303.          */
  1304.         printDevice = ((**hPrint).prStl.wDev >> 8) & 0xFF;
  1305.         draftMode = (**hPrint).prJob.bJDocLoop == bDraftLoop;
  1306.         if (draftMode && printDevice == ImageWriter)
  1307.             nCopies = (**hPrint).prJob.iCopies;
  1308.         else {
  1309.             nCopies = 1;
  1310.         }
  1311.         /*
  1312.          * Printing begins here.  PrOpenDoc sets the port.
  1313.          */
  1314.         printPort = PrOpenDoc(hPrint, NULL, NULL);
  1315.         docIsOpen = TRUE;
  1316.         CheckError(PrError());
  1317.         for (iCopy = 1; iCopy <= nCopies; iCopy++) {
  1318.             for (page = (**hPrint).prJob.iFstPage;
  1319.                         page <= (**hPrint).prJob.iLstPage;
  1320.                         page++) {
  1321.                 SetCursor(*GetCursor(watchCursor));
  1322.                 /*
  1323.                  * Print the current page.
  1324.                  */
  1325.                 PrOpenPage(printPort, NULL);
  1326.                 pageIsOpen = TRUE;
  1327.                 pageRect = (**hPrint).prInfo.rPage;
  1328.                 status = (*userPageProc)(
  1329.                             hPrint,
  1330.                             fontNumber,
  1331.                             fontSize,
  1332.                             clientData,
  1333.                             &pageRect,
  1334.                             page
  1335.                         );
  1336.                 PrClosePage(printPort);
  1337.                 pageIsOpen = FALSE;
  1338.                 CheckError(status);
  1339.             }
  1340.         }
  1341.         SetPort(savePort);
  1342.         PrCloseDoc(printPort);
  1343.         docIsOpen = FALSE;
  1344.         status = PrError();
  1345.         if (draftMode == FALSE && status == noErr) {
  1346.             PrPicFile(hPrint, NULL, NULL, NULL, &printStatus);
  1347.             status = PrError();
  1348.         }
  1349.         /*
  1350.          * Everyone exits here
  1351.          */
  1352. exit:    SetPort(savePort);
  1353.         InitCursor();
  1354.         if (pageIsOpen)
  1355.             PrClosePage(printPort);
  1356.         if (docIsOpen)
  1357.             PrCloseDoc(printPort);
  1358.         if (printIsOpen)
  1359.             PrClose();
  1360.         if (ourPrintHandle && hPrint != NULL)
  1361.             DisposHandle((Handle) hPrint);
  1362.         return (status);
  1363. }
  1364.  
  1365.